package com.lx.pc.restful.interceptor;

import java.io.IOException;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.util.MultiValueMap;
import org.springframework.validation.BindingResult;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.lx.pc.utils.Utils;

/**
 * 日志拦截器
 * 
 * @author martin_duan
 *
 */
public class LoggerInterceptor implements HandlerInterceptor, ApplicationContextAware, InitializingBean {

	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	private static final String DEFAULT_EXCLUSIVE_PATH = "/backstage/system/log/";

	private static final String DEFAULT_ENCRYPT_FIELD = "password";

	private static final String DEFAULT_ENCRYPT_SECRET_KEY = "Z71XxeObrIETk+wR9CDRlg==";

	private ApplicationContext applicationContext;
	
	private volatile ThreadLocal<T> threadLoggerParam = new ThreadLocal<>();

	private volatile ThreadLocal<JSONObject> threadJSONParam = new ThreadLocal<JSONObject>();

	/**
	 * 不进行日志拦截路径
	 */
	private String exclusivePath = DEFAULT_EXCLUSIVE_PATH;

	private String[] exclusivePaths = this.exclusivePath.split(",");

	/**
	 * 加密字段
	 */
	private String encryptField = DEFAULT_ENCRYPT_FIELD;

	/**
	 * 加密秘钥
	 */
	private String encryptSecretKey = DEFAULT_ENCRYPT_SECRET_KEY;

	/**
	 * 加密字段匹配模式
	 */
	private Pattern pattern = Pattern.compile("\"" + encryptField + "\":\"(.*?)\"");

	public String getExclusivePath() {
		return exclusivePath;
	}

	public void setExclusivePath(String exclusivePath) {
		this.exclusivePath = exclusivePath;
	}

	public String getEncryptField() {
		return encryptField;
	}

	public void setEncryptField(String encryptField) {
		this.encryptField = encryptField;
	}

	public String getEncryptSecretKey() {
		return encryptSecretKey;
	}

	public void setEncryptSecretKey(String encryptSecretKey) {
		this.encryptSecretKey = encryptSecretKey;
	}

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		if (StringUtils.isNotEmpty(this.exclusivePath)) {
			this.exclusivePaths = this.exclusivePath.split(",");
		}
		if (StringUtils.isNotEmpty(this.encryptField)) {
			this.pattern = Pattern.compile("\"" + this.encryptField + "\":\"(.*?)\"");
		}
	}

	private boolean isRecord(String url) {
		if (this.exclusivePaths != null && this.exclusivePaths.length > 0) {
			for (String path : exclusivePaths) {
				if (url.contains(path)) {
					return false;
				}
			}
		}
		return true;
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		try {
			if (isRecord(request.getRequestURL().toString())) {
				JSONObject param = new JSONObject();
				if (MultipartHttpServletRequest.class.isAssignableFrom(request.getClass())) {
					MultiValueMap<String, MultipartFile> multiValueMap = ((MultipartHttpServletRequest) request).getMultiFileMap();
					if (!multiValueMap.isEmpty()) {
						JSONArray array = new JSONArray();
						for (Entry<String, List<MultipartFile>> entry : multiValueMap.entrySet()) {
							List<MultipartFile> multipartFiles = entry.getValue();
							for (MultipartFile multipartFile : multipartFiles) {
								JSONObject object = new JSONObject();
								try {
									object.put("content", Base64.encodeBase64String(multipartFile.getBytes()));
									object.put("contentType", multipartFile.getContentType());
									object.put("fileName", multipartFile.getName());
									object.put("originalFilename", multipartFile.getOriginalFilename());
									object.put("size", multipartFile.getSize());
								} catch (IOException e) {
									logger.error(e.getMessage(), e);
								} finally {
									array.add(object);
								}
							}
						}
						param.put("files", array);
					}
				}
				Enumeration<String> names = request.getParameterNames();
				while (names.hasMoreElements()) {
					String name = names.nextElement();
					if (request.getParameterValues(name).length > 1) {
						JSONArray array = new JSONArray();
						String[] values = request.getParameterValues(name);
						for (String value : values) {
							array.add(value);
						}
						param.put(name, array);
					} else {
						param.put(name, request.getParameter(name));
					}
				}
				JSONObject content = new JSONObject();
				content.put("param", param);
				threadJSONParam.set(content);
			}
		} catch (Exception e) {
			logger.error(e.getMessage());
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
		try {
			if (modelAndView != null && !modelAndView.getModel().isEmpty() && threadLoggerParam.get() != null) {
				JSONObject content = threadJSONParam.get();
				if (content == null) {
					content = new JSONObject();
					threadJSONParam.set(content);
				}
				JSONObject param = new JSONObject();
				if (content.containsKey("param")) {
					param = content.getJSONObject("param");
				}
				JSONObject result = new JSONObject();
				for (Entry<String, Object> entry : modelAndView.getModel().entrySet()) {
					if (entry.getKey().contains(BindingResult.MODEL_KEY_PREFIX) && BindingResult.class.isAssignableFrom(entry.getValue().getClass())) {
						BindingResult bindingResult = (BindingResult) entry.getValue();
						Map<String, Object> model = bindingResult.getModel();
						for (Entry<String, Object> modelEntry : model.entrySet()) {
							if (!modelEntry.getKey().equals(entry.getKey())) {
								param.put(modelEntry.getKey(), modelEntry.getValue());
							}
						}
					} else if (!entry.getKey().contains("springframework")) {
						result.put(entry.getKey(), entry.getValue());
					}
				}
				content.put("param", param);
				content.put("result", result);
			}
		} catch (Exception e) {
			throw e;
		} finally {
			threadJSONParam.remove();
		}
	}
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
		try {
//			LoggerVo log = threadLoggerParam.get();
//			if (log != null) {
//				StringBuilder buffer = new StringBuilder();
//				buffer.append(StringUtils.defaultString(log.getIp(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getContext(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getContentType(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getScheme(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getMethod(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getLocation(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getAgent(), "") + "\r\n");
//				buffer.append(StringUtils.defaultString(log.getParam(), ""));
//				log.setContent(buffer.toString());
//				this.getLoggerService().save(log);
//			}
		} catch (Exception e) {
			throw e;
		} finally {
			threadLoggerParam.remove();
		}
	}
	
	private String getRemoteIP(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}
		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();
		}
		return ip;
	}

	/**
	 * 替换所有加密字段
	 * 
	 * @param content
	 * @return
	 */
	private String replaceAll(String content) {
		if (StringUtils.isNotEmpty(content) && StringUtils.isNotEmpty(this.encryptField)) {
			Matcher matcher = pattern.matcher(content);
			StringBuffer buffer = new StringBuffer();
			while (matcher.find()) {
				matcher.appendReplacement(buffer, "\"" + this.encryptField + "\":\"" + Utils.encrypt(matcher.group(1), this.encryptSecretKey) + "\"");
			}
			matcher.appendTail(buffer);
			return buffer.toString();
		}
		return content;
	}


}
